// exception.cc 
//	Entry point into the Nachos kernel from user programs.
//	There are two kinds of things that can cause control to
//	transfer back to here from user code:
//
//	syscall -- The user code explicitly requests to call a procedure
//	in the Nachos kernel.  Right now, the only function we support is
//	"Halt".
//
//	exceptions -- The user code does something that the CPU can't handle.
//	For instance, accessing memory that doesn't exist, arithmetic errors,
//	etc.  
//
//	Interrupts (which can also cause control to transfer from user
//	code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "syscall.h"
#include <stdio.h>
#include <iostream>
#include <time.h>
#include <stdlib.h>
#include "process.h"
#include "../network/post.h"

using namespace std;

#ifdef NETWORK	
#define SEND_AND_RECEIVE()			\
	PacketHeader pktHdrIn;			\
	PacketHeader pktHdrOut;			\
	MailHeader mailHdrIn;			\
	MailHeader mailHdrOut;			\
	char incomingMsg[MaxMailSize];	\
	bool returnVal;														\
	pktHdrIn.to = 1;															\
	pktHdrIn.from = 0;															\
	pktHdrIn.length = MaxMailSize;												\
	mailHdrIn.to = 1;															\
	mailHdrIn.from = 0;															\
	mailHdrIn.length = MaxMailSize;												\
	pktHdrOut.to = 0;															\
	mailHdrOut.to = 0;															\
	mailHdrOut.from = currentThread->mailboxID;									\
	mailHdrOut.length = strlen(outgoingMsg) + 1;								\
	returnVal = postOffice->Send(pktHdrOut, mailHdrOut, outgoingMsg);			\
	if (!returnVal)																\
	{																			\
		DEBUG('n', "POST OFFICE FAILED TO SEND MESSAGE\n");						\
		interrupt->Halt();														\
	}																			\
	printf("Waiting to receive a message from the server.\n");					\
	postOffice->Receive(currentThread->mailboxID, &pktHdrIn, &mailHdrIn, incomingMsg);
#endif

int copyin(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes from the current thread's virtual address vaddr.
    // Return the number of bytes so read, or -1 if an error occors.
    // Errors can generally mean a bad virtual address was passed in.
    bool result;
    int n=0;			// The number of bytes copied in
    int *paddr = new int;

    while ( n >= 0 && n < len) {
      result = machine->ReadMem( vaddr, 1, paddr );
      while(!result) // FALL 09 CHANGES
	  {
   			result = machine->ReadMem( vaddr, 1, paddr ); // FALL 09 CHANGES: TO HANDLE PAGE FAULT IN THE ReadMem SYS CALL
	  }	
      
      buf[n++] = *paddr;
     
      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }

    delete paddr;
    return len;
}

int copyout(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes to the current thread's virtual address vaddr.
    // Return the number of bytes so written, or -1 if an error
    // occors.  Errors can generally mean a bad virtual address was
    // passed in.
    bool result;
    int n=0;			// The number of bytes copied in

    while ( n >= 0 && n < len) {
      // Note that we check every byte's address
      result = machine->WriteMem( vaddr, 1, (int)(buf[n++]) );

      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }

    return n;
}

void Create_Syscall(unsigned int vaddr, int len) {
    // Create the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  No
    // way to return errors, though...
    char *buf = new char[len+1];	// Kernel buffer to put the name in

    if (!buf) return;

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Create\n");
	delete buf;
	return;
    }

    buf[len]='\0';

    fileSystem->Create(buf,0);
    delete[] buf;
    return;
}

int Open_Syscall(unsigned int vaddr, int len) {
    // Open the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  If
    // the file is opened successfully, it is put in the address
    // space's file table and an id returned that can find the file
    // later.  If there are any errors, -1 is returned.
    char *buf = new char[len+1];	// Kernel buffer to put the name in
    OpenFile *f;			// The new open file
    int id;				// The openfile id

    if (!buf) {
	printf("%s","Can't allocate kernel buffer in Open\n");
	return -1;
    }

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Open\n");
	delete[] buf;
	return -1;
    }

    buf[len]='\0';

    f = fileSystem->Open(buf);
    delete[] buf;

    if ( f ) {
	if ((id = currentThread->space->fileTable.Put(f)) == -1 )
	    delete f;
	return id;
    }
    else
	return -1;
}

void Write_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one. For disk files, the file is looked
    // up in the current address space's open file table and used as
    // the target of the write.
    
    char *buf;		// Kernel buffer for output
    OpenFile *f;	// Open file for output

    if ( id == ConsoleInput) return;
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer for write!\n");
	return;
    } else {
        if ( copyin(vaddr,len,buf) == -1 ) {
	    printf("%s","Bad pointer passed to to write: data not written\n");
	    delete[] buf;
	    return;
	}
    }

    if ( id == ConsoleOutput) {
      for (int ii=0; ii<len; ii++) {
	printf("%c",buf[ii]);
      }

    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    f->Write(buf, len);
	} else {
	    printf("%s","Bad OpenFileId passed to Write\n");
	    len = -1;
	}
    }

    delete[] buf;
}

int Read_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one.    We reuse len as the number of bytes
    // read, which is an unnessecary savings of space.
    char *buf;		// Kernel buffer for input
    OpenFile *f;	// Open file for output

    if ( id == ConsoleOutput) return -1;
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer in Read\n");
	return -1;
    }

    if ( id == ConsoleInput) {
      //Reading from the keyboard
      scanf("%s", buf);

      if ( copyout(vaddr, len, buf) == -1 ) {
	printf("%s","Bad pointer passed to Read: data not copied\n");
      }
    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    len = f->Read(buf, len);
	    if ( len > 0 ) {
	        //Read something from the file. Put into user's address space
  	        if ( copyout(vaddr, len, buf) == -1 ) {
		    printf("%s","Bad pointer passed to Read: data not copied\n");
		}
	    }
	} else {
	    printf("%s","Bad OpenFileId passed to Read\n");
	    len = -1;
	}
    }

    delete[] buf;
    return len;
}

void Close_Syscall(int fd) {
    // Close the file associated with id fd.  No error reporting.
    OpenFile *f = (OpenFile *) currentThread->space->fileTable.Remove(fd);

    if ( f ) {
      delete f;
    } else {
      printf("%s","Tried to close an unopen file\n");
    }
}

/*
 * This method is called by the exec syscall to fork a exec_thread.
 * 1. First acquire the process table lock.
 * 2. Call InitRegisters to set up the stack. Initregister will use the start of stack pointer instead of numPages.
 * 3. Restore state, release process table lock, and call machine->run to put the thread on the ready queue. 
*/
void Exec_Thread(int dummy)
{
	processTableLock->Acquire();
	currentThread->space->InitRegisters();
	currentThread->space->RestoreState();
	processTableLock->Release();
	machine->Run();
}

/*
 * This method is the exec syscall used to execute a user program.
 * 1. First check if the length passed in is less than or equal to 0, if so, return -1, else acquire the processtable lock. 
 * 2. Create a OpenFile pointer used to open the file. Allocate the char buffer and copy in the name. 
 * 3. If copyin fails, return -1, otherwise, call fileSystem->Open with the buffer and store it in the OpenFile pointer.
 * 4. If fileSystem->Open call succeeded, Create a new process, create the address space for the process. 
 * 5. Create the main thread for the process and set it's address space to the current address space. 
 * 6. Add the process to the process table. If operation successful, set the stack pointer at the time of allcoation and fork the Exec_Thread.
 * 7. Else return -1. 
*/
SpaceId Exec_Syscall(int vaddr, int len)
{
	printf("got in ExecSyscall.\n");
	if (len <= 0)
	{
		DEBUG('a', "string length less than or equal to zero. Cannot fork thread");
		printf("bad length in exec syscall.\n");
		return -1;
	}
	processTableLock->Acquire();
	OpenFile* exe;
	char *buf = new char[len+1];
	if(copyin(vaddr,len,buf) == -1)
	{
		DEBUG('a', "Cannot copy characters into the buffer\n");
		printf("cannot copy character into the buffer.\n");
		delete[] buf;
		processTableLock->Release();
		return -1;
	}
	else
	{
		buf[len]='\0';
		printf("setting exe to:[%s].\n", buf);
		exe = fileSystem->Open(buf);
		delete[] buf;
	}
	//if exe successfully opened
	if(exe)
	{
		Process *myProcess = new Process;
		myProcess->childrenThreadCount = 0;
		AddrSpace *myAddressSpace = new AddrSpace(exe,myProcess);
		int processTableIndex;
		//Create the parent thread of the process
		Thread *t = new Thread(buf);
		t->space = myAddressSpace;
		t->mailboxID = 0;
		processTableIndex = ProcessTable->Put(myAddressSpace);
		//If the process table returns a valid ID
		//assign the proper addressspace id. 
		if(processTableIndex != -1)
		{
			myAddressSpace->space = processTableIndex;
			myProcess->startOfStack = myProcess->startOfStack + divRoundUp(UserStackSize,PageSize);
			//This always forks the very first thread in the process
			t->Fork(Exec_Thread, 0);
			processTableLock->Release();
			return processTableIndex;
		}
		else
		{
			DEBUG('a', "Process Table did not return a valid ID.\n");
			processTableLock->Release();
			return -1;
		}
	}
	else
	{
			DEBUG('a', "Executable NOT executed.\n");
			printf("invalid exe passed in to exec syscall.\n");
			processTableLock->Release();
			return -1;
	}
}

/*
 * This method is called by the fork syscall to fork a kernel_thread.
 * 1. First acquire the process table lock.
 * 2. Make a process pointer and set the current process to our pointer. 
 * 3. Set the start of stack at the time of allocation
 * 4. Call InitRegisters to set up the stack. Initregister will use the start of stack pointer instead of numPages.
 * 5. Call machine write register to set up the stack
 * 6. Restore state, release process table lock, and call machine->run to put the thread on the ready queue. 
*/
void Kernel_Thread(int vaddr)
{
	processTableLock->Acquire();
	Process *thisProcess = currentThread->space->myProcess;
	thisProcess->startOfStack = thisProcess->startOfStack + divRoundUp(UserStackSize, PageSize);
	currentThread->space->InitRegisters();
	
	machine->WriteRegister(PCReg, vaddr);
	machine->WriteRegister(NextPCReg, vaddr + 4);
	DEBUG('c', "Forking thread at PCReg 0x%x and StackReg at 0x%x\n", vaddr, machine->ReadRegister(StackReg));
	
    currentThread->space->RestoreState();	// restore state for context switch. 
	processTableLock->Release();	
    machine->Run();			// run the userprog / thread.
    ASSERT(FALSE);			// we should NEVER get here.
}

/*
 * This method is called to fork a thread.
 * 1. First it checks if length of the string passed in is less than 0, if so, failed, return -1. 
 * 2. Allocate the char buffer and attempt to copy the name passed in to the buffer. if copyin fails, return -1. 
 * 3. Check if the vaddr passed in is word aligned. If not, return -1.
 * 4. If all checks have passed, Create a process pointer and set that pointer to the current process. 
 * 5. Create a new thread and use the name from the buffer, and set the thread address space to the current address space of the process
 * 6. Increment the children thread counter of the process, Fork the thread with Kernel_Thread
 * 7. delete buffer and return 1. 
*/
int Fork_Syscall(int vaddr, int name, int len)
{
	if (len <= 0)
	{
		DEBUG('a', "string length less than or equal to zero. Cannot fork thread");
		return -1;
	}
	
	char *buf = new char[len + 1];
	if( copyin(name, len, buf) == -1 )
	{
		DEBUG('a', "Cannot copy characters into the buffer\n");
		delete[] buf;
		return -1;
	}
	if ((vaddr % 4) != 0)
	{
		DEBUG('a', "Vaddr of function is not aligned");
		delete[] buf;
		return -1;
	}
	buf[len]='\0';
	Process *p = currentThread->space->myProcess;
	Thread *t = new Thread(buf);
	t->space = p->processAddressSpace;
	t->mailboxID = p->childrenThreadCount + 1;
	p->childrenThreadCount++;
	t->Fork(Kernel_Thread, vaddr);
	delete[] buf;
	return 1;
}

//Yield syscall, just yields the thread.
void Yield_Syscall()
{
	currentThread->Yield();
}

/*
 * This is the exit system call. 
 * 1. Acquire the process table lock
 * 2. Check if the thread is the last process of the last thread, if so, call interrupt->halt
 * 3. Else if check if the thread is the last thread of any process, if so, remove from process table, give OS resources back
 * 4. Else If it's any other thread, terminate the thread. 
*/
void Exit_Syscall(int dummy)
{
#ifdef NETWORK
		printf("In exeception.cc ifdef NETWORK EXIT.\n");
		char outgoingMsg[MaxMailSize];
		sprintf(outgoingMsg, "%d^%d", SC_Exit, currentThread->mailboxID);
		SEND_AND_RECEIVE();
		interrupt->Halt();
#else
	
	processTableLock->Acquire();
	if( ProcessTable->numProcess ==1 && currentThread->space->myProcess->childrenThreadCount == 1 )
	{
		printf("got exit inside case 1.\n");
		//Last thread in the last process
		printf("This is the last thread in the last process. Terminating Nachos.\n");
		processTableLock->Release();

		
		interrupt->Halt();
	}
	else if( currentThread->space->myProcess->childrenThreadCount == 1)
	{
		printf("got inside exit case 2.\n");
		//Last thread in any process
		printf("This is the Last thread of a process. Terminating the process.\n");
		ProcessTable->Remove(currentThread->space->space);
		delete currentThread->space;
		processTableLock->Release();
		currentThread->Finish();
	}
	else
	{
		//any other thread
		DEBUG('a',"This is not the last thread of any process. Now terminating thread.\n");
		currentThread->space->myProcess->childrenThreadCount--;
		processTableLock->Release();
		currentThread->Finish();
	}
#endif
}


//=================================================//
//----------------LOCK SYSCALLS--------------------//
//=================================================//
/*
 * This method is called to find a available lock to create. 
 * It returns the index of the first lock that it finds to be null and returns it's index
 * returns -1 if failed to find a lock. 
*/
int findLock()
{
	int result = -1;
	for (int i = 0; i < MAX_LOCKS; i++)
	{
		if (LockTable[i].lock == NULL)
		{
			//if the lock is null, make this our new lock.
			//pass the index of this lock. 
			result = i;
			break;
		}
	}
	return result;
}

/*
 * This method is called to validate the ID of the lock:
 * 1. First it checks if the lock is null. If so, return false.
 * 2. Check if the ID is within the bounds of the LockTable. If so, return false.
 * 3. Check if the lock is in the same address space as the current thread. If so, return false.
 * 4. return true if it passes the test. 
*/
bool LockValidation(int lockID)
{
#ifdef NETWORK
	if (lockID < 0 || lockID >= 1000)
	{
		DEBUG('c', "The lock ID is invalid.\n");
		return false;
	}
	return true;
#else
	LockTableLock->Acquire();
	//check if lock is null
	if (LockTable[lockID].lock == NULL)
	{
		DEBUG('c', "The lock is null.\n");
		LockTableLock->Release();
		return false;
	}
	//check if lock is less than 0 or more than the maximum number of locks, invalid lock
	if (lockID < 0 || lockID >= MAX_LOCKS)
	{
		DEBUG('c', "The Lock index is not within range. \n");
		LockTableLock->Release();
		return false;
	}
	//check if the lock is in the same address space, if not, invalid lock
	if (LockTable[lockID].addrSpace != (int)(currentThread->space))
	{
		DEBUG('c', "The lock is not in the current address space.\n");
		LockTableLock->Release();
		return false;
	}
	//Lock is good if it gets here.
	LockTableLock->Release();
	return true;
#endif
}

/*
 * This method is called create a lock. 
 * 1. First it checks if the length of the name that's passed in is greater than 0. If not, return -1. 
 * 2. acquire the LockTableLock and allocate the character array for the name. 
 * 3. Copy the name into the character array, if failed, return -1. 
 * 4. Check if we've already allocated the maximum number of locks, if so, find a free lock. 
 * 5. If we have no allocated the maximum number of locks, make the new lock here and increment nextLock appropriately.
*/
int CreateLock_Syscall(unsigned int vaddr, int len)
{
	//If the length is less than or equal to 0, return -1, dont allocate.
	//dont create lock. 
	if (len <= 0)
	{
		DEBUG('a', "Length is less than or equal to 0, cannot allocate string");
		return -1;
	}
	//acquire the lock for the lock table to access it's resources
	
	char *lockName = new char[len + 1]; // Kernel buffer to put the name in
	
	//copy the vaddr passed in to the character array. 
	if (copyin(vaddr, len + 1, lockName) == -1)
	{
		DEBUG('a', "Name was not copied onto the character array.\n");
		delete[] lockName;
		//LockTableLock->Release();
		return -1;
	}
	lockName[len]='\0';	
#ifdef NETWORK
		printf("In exeception.cc ifdef NETWORK CreatLock. with name [%s].\n", lockName);
		char outgoingMsg[MaxMailSize];
		sprintf(outgoingMsg, "%d^%d^%s^", SC_CreateLock, currentThread->mailboxID, lockName);
		SEND_AND_RECEIVE();
		return atoi(incomingMsg);
#else
	//if we already made a thousand locks, find free ones from the list. 
	LockTableLock->Acquire();
	if(nextLock >= MAX_LOCKS)
	{
		int freeLock = findLock();
		if( freeLock != -1 )
		{
			LockTable[freeLock].lock = new Lock(lockName);
			LockTable[freeLock].addrSpace = (int)currentThread->space;
			LockTable[freeLock].isDeleted = false;
			LockTable[freeLock].isToBeDeleted = false;
			LockTable[freeLock].acquireCount = 0;
			
			LockTableLock->Release();
			return freeLock;
		}
		else // This means findLock failed. 
		{
			DEBUG('a', "CreateLock_Syscall: There are no more locks\n");
			LockTableLock->Release();
			return -1;
		}
	}
	//make a new lock here.
	if(LockTable[nextLock].lock == NULL)
	{
		LockTable[nextLock].lock = new Lock(lockName);
		LockTable[nextLock].addrSpace = (int)currentThread->space;
		LockTable[nextLock].isDeleted = false;
		LockTable[nextLock].isToBeDeleted = false;
		LockTable[nextLock].acquireCount = 0;
		
		nextLock++;
		LockTableLock->Release();
		//printf("CREATED Lock with index[%d]\n", nextLock-1);
		return (nextLock - 1);
	}
	return -1;
#endif
}

/*
 * This method is called to delete the lock with the given lockID:
 * 1. First acquire the lock table lock. 
 * 2. First check if this is a valid lock, if so, check if lock is busy and check if anyones trying to acquire it. if not, delete the lock
 * 3. If the lock is not free, then mark the lock to be deleted.
 * 4. Else if the lock is not valid, return 0, failed to delete lock. 
*/
int DeleteLock_Syscall(int lockID)
{
	
	//If the lock is valid, check further.
	if(LockValidation(lockID))
	{
#ifdef NETWORK
		printf("In exeception.cc ifdef NETWORK DeleteLock.\n");
		char outgoingMsg[MaxMailSize];
		sprintf(outgoingMsg, "%d^%d^%d", SC_DeleteLock, currentThread->mailboxID, lockID);
		SEND_AND_RECEIVE();
		return 1;
#else
		LockTableLock->Acquire();
		// If the lock is not busy, and no one is trying to acquire it, delete the lock
		if (!LockTable[lockID].lock->isBusy() && LockTable[lockID].acquireCount == 0)
		{
			DEBUG('a', "Deleting Lock [%d].\n", lockID);
			//printf("Deleting Lock [%d]. \n", lockID);
			LockTable[lockID].addrSpace = 0;
			LockTable[lockID].isDeleted = true;
			LockTable[lockID].isToBeDeleted = false;
			delete LockTable[lockID].lock;
			LockTable[lockID].lock = NULL;			
		}
		// If the lock is being used, mark it to be deleted
		else
		{
			DEBUG('a', "Lock [%d] has been marked for deletion.\n", lockID);
			LockTable[lockID].isToBeDeleted = true;
		}
		LockTableLock->Release();
		//printf("Lock [%d] deleted. \n", lockID);
		return 1;
#endif
	}
	//If the lock is not valid, return 0, failed to delete lock.
	else
	{
		DEBUG('a', "Failed deleting Lock [%d].\n", lockID);
		//printf("Failed to delete lock in DeleteLock_Syscall\n");
		return 0;
	}
}

/*
 * This method is called to acquire the lock with the given lockId:
 * 1. First acquire the lock table lock.
 * 2. If the lock is valid, try to acquire the lock. 
 * 3. If not, release lock table lock, return 0, acquire failed.
*/
int AcquireLock_Syscall(int lockID)
{
	if (LockValidation(lockID))
	{
#ifdef NETWORK
		printf("In exeception.cc ifdef NETWORK AcquireLock.\n");
		char outgoingMsg[MaxMailSize];
		sprintf(outgoingMsg,"%d^%d^%d", SC_AcquireLock, currentThread->mailboxID, lockID);
		SEND_AND_RECEIVE();
		return 1;
#else
		LockTableLock->Acquire();
		DEBUG('a', "Acquiring Lock [%d].\n", lockID);
		//printf("Acquiring Lock [%d].\n", lockID);
		//If it's a good lock, try to acquire it. 
		//increment the acquireCount by 1 before attemping to get the lock
		LockTable[lockID].acquireCount++;
		LockTableLock->Release();
		//Acquire the lock here. 
		LockTable[lockID].lock->Acquire();
		//Lock acquired, decrement the acquireCount by 1 because we're no longer trying to acquire it. 
		LockTable[lockID].acquireCount--;	
		return 1; 
#endif
	}
	else
	{
		DEBUG('a', "Failed acquiring Lock [%d].\n", lockID);
		return 0;
	}	
}

/*
 * This method is called to release the lock with the given lockId:
 * 1. First acquire the lock table lock.
 * 2. If the lock is valid, release the lock. 
 * 3. If the lock has been marked to be deleted, check if it can be deleted, if so, delete it.
 * 4. Else, we're done, return 1. 
 * 5. If lock is not valid, return -1, failed to release lock. 
*/
int ReleaseLock_Syscall(int lockID)
{
	
	if (LockValidation(lockID))
	{
#ifdef NETWORK
		printf("In exeception.cc ifdef NETWORK ReleaseLock.\n");
		char outgoingMsg[MaxMailSize];
		sprintf(outgoingMsg,"%d^%d^%d", SC_ReleaseLock, currentThread->mailboxID, lockID);
		SEND_AND_RECEIVE();
		return 1;
#else
		LockTableLock->Acquire();
		//release the lock here if the lock is valid
		//printf("Releasing Lock [%d].\n", lockID);
		LockTable[lockID].lock->Release();
		//If the lock have been marked to be deleted
		if (!LockTable[lockID].lock->isBusy() && LockTable[lockID].isToBeDeleted && LockTable[lockID].acquireCount == 0)
		{
			DEBUG('a', "Deleting Lock [%d].\n", lockID);
			//printf("Lock has been marked for deletion, deleting lock[%d].\n", lockID);
			LockTable[lockID].addrSpace = 0;
			LockTable[lockID].isDeleted = true;
			LockTable[lockID].isToBeDeleted = false;
			delete LockTable[lockID].lock;
			LockTable[lockID].lock = NULL;	
		}
		LockTableLock->Release();
		return 1; 
#endif //END NETWORK
	}
	else
	{
		DEBUG('a', "Failed releasing Lock [%d].\n", lockID);
		return 0;
	}
}
//=================================================//
//--------------(END) LOCK SYSCALLS----------------//
//=================================================//

//=================================================//
//------------------CV SYSCALLS--------------------//
//=================================================//
/*
 * This method is called to find a available cv to create. 
 * It returns the index of the first cv that it finds to be null and returns it's index
 * returns -1 if failed to find a cv. 
*/
int findCV()
{
	int result = -1;
	for (int i = 0; i < MAX_CVS; i++)
	{
		if (CVTable[i].cv == NULL)
		{
			//if the cv is null, make this our new cv.
			//pass the index of this cv. 
			result = i;
			break;
		}
	}
	return result;
}

/*
 * This method is called to validate the ID of the cv:
 * 1. First it checks if the cv is null. If so, return false.
 * 2. Check if the ID is within the bounds of the CVTable. If so, return false.
 * 3. Check if the cv is in the same address space as the current thread. If so, return false.
 * 4. return true if it passes the test. 
*/
bool CVValidation(int cvID)
{
#ifdef NETWORK
	if (cvID < 0 || cvID >= 1000)
	{
		DEBUG('c', "The cv ID is invalid.\n");
		return false;
	}
	return true;
#else
	CVTableLock->Acquire();
	//check if cv is null
	if (CVTable[cvID].cv == NULL)
	{
		DEBUG('c', "The cv is null.\n");
		CVTableLock->Release();
		return false;
	}
	//check if cvID is less than 0 or more than the maximum number of cv, invalid cv
	if (cvID < 0 || cvID >= MAX_CVS)
	{
		DEBUG('c', "The cv index is not within range. \n");
		CVTableLock->Release();
		return false;
	}
	//check if the cv is in the same address space, if not, invalid cv
	if (CVTable[cvID].addrSpace != (int)(currentThread->space))
	{
		DEBUG('c', "The cv is not in the current address space.\n");
		CVTableLock->Release();
		return false;
	}
	//cv is good if it gets here.
	CVTableLock->Release();
	return true;
#endif
}

/*
 * This method is called create a cv. 
 * 1. First it checks if the length of the name that's passed in is greater than 0. If not, return -1. 
 * 2. acquire the CVTableLock and allocate the character array for the name. 
 * 3. Copy the name into the character array, if failed, return -1. 
 * 4. Check if we've already allocated the maximum number of cvs, if so, find a free lock. 
 * 5. If we have no allocated the maximum number of cvs, make the new cv here and increment nextCV appropriately.
*/
int CreateCV_Syscall(unsigned int vaddr, int len)
{
	//If the length is less than or equal to 0, return -1, dont allocate.
	//dont create cv. 
	if (len <= 0)
	{
		DEBUG('a', "Length is less than or equal to 0, cannot allocate string for CV");
		return -1;
	}
	//acquire the cv for the cv table to access it's resources
	
	char *cvName = new char[len + 1]; // Kernel buffer to put the name in
	
	//copy the vaddr passed in to the character array. 
	if (copyin(vaddr, len + 1, cvName) == -1)
	{
		DEBUG('a', "Name was not copied onto the character array for the CV.\n");
		delete[] cvName;
		CVTableLock->Release();
		return -1;
	}
	cvName[len]='\0';
#ifdef NETWORK
		char outgoingMsg[MaxMailSize];
		sprintf(outgoingMsg, "%d^%d^%s", SC_CreateCondition, currentThread->mailboxID, cvName);
		SEND_AND_RECEIVE();
		return atoi(incomingMsg);
#else	
	CVTableLock->Acquire();
	//if we already made a thousand cvs, find free ones from the list. 
	if(nextCV >= MAX_CVS)
	{
		int freeCV = findCV();
		if( freeCV != -1 )
		{
			CVTable[freeCV].cv = new Condition(cvName);
			CVTable[freeCV].addrSpace = (int)currentThread->space;
			CVTable[freeCV].isDeleted = false;
			CVTable[freeCV].isToBeDeleted = false;
			CVTable[freeCV].waitCount = 0;
			
			CVTableLock->Release();
			return freeCV;
		}
		else // This means findCV failed. 
		{
			DEBUG('a', "CreateCV_Syscall: There are no more free cvs\n");
			CVTableLock->Release();
			return -1;
		}
	}
	//make a new cv here.
	if(CVTable[nextCV].cv == NULL)
	{
		CVTable[nextCV].cv = new Condition(cvName);
		CVTable[nextCV].addrSpace = (int)currentThread->space;
		CVTable[nextCV].isDeleted = false;
		CVTable[nextCV].isToBeDeleted = false;
		CVTable[nextCV].waitCount = 0;
		
		nextCV++;
		CVTableLock->Release();
		//printf("Created a new CV [%d]]\n", nextCV-1);
		return (nextCV - 1);
	}
	return -1;
#endif
}

/*
 * This method is called to delete the cv with the given cvID:
 * 1. First acquire the cv table lock. 
 * 2. First check if this is a valid cv, if so, check if cv is busy and check if anyones trying to waiting on it. if not, delete the cv
 * 3. If the cv is not free, then mark the cv to be deleted.
 * 4. Else if the lock is not valid, return 0, failed to delete lock. 
*/
int DeleteCV_Syscall(int cvID)
{
	
	//If the cv is valid, check further.
	if(CVValidation(cvID))
	{
#ifdef NETWORK
		char outgoingMsg[MaxMailSize];
		sprintf(outgoingMsg, "%d^%d^%d", SC_DeleteCondition, currentThread->mailboxID, cvID);
		SEND_AND_RECEIVE();
		return 1;
#else
		CVTableLock->Acquire();
		// If the cv's waitqueue is empty, and no one is waiting on it, delete the cv
		if (CVTable[cvID].cv->isEmpty() && CVTable[cvID].waitCount == 0)
		{
			DEBUG('a', "Deleting cv [%d].\n", cvID);
			//printf("Deleting CV[%d].\n", cvID);
			CVTable[cvID].addrSpace = 0;
			CVTable[cvID].isDeleted = true;
			CVTable[cvID].isToBeDeleted = false;
			delete CVTable[cvID].cv;
			CVTable[cvID].cv = NULL;			
		}
		// If the cv is being used, mark it to be deleted
		else
		{
			DEBUG('a', "CV [%d] will be deleted later.\n", cvID);
			CVTable[cvID].isToBeDeleted = true;
		}
		CVTableLock->Release();
		//printf("CV [%d] deleted.\n", cvID);
		return 1;
#endif
	}
	//If the cv is not valid, return 0, failed to delete cv.
	else
	{
		DEBUG('a', "Failed deleting cv [%d].\n", cvID);
		return 0;
	}
}

/*
 * This method is called to wait on the cv with the given cvID and lockID:
 * 1. First acquire the cv table lock and the lock table lock. 
 * 2. First check if this is a valid cv / lock, if so, acquire the lock and wait on the CV. 
 * 3. Check if the CV has been marked for deletion if so, delete the cv
 * 4. Else if the cv / lock is not valid, return 0, failed operation.  
*/
int WaitCV_Syscall(int cvID, int lockID)
{	
	if (LockValidation(lockID) && CVValidation(cvID))
	{
#ifdef NETWORK
		char outgoingMsg[MaxMailSize];
		sprintf(outgoingMsg,"%d^%d^%d^%d", SC_WaitCondition, currentThread->mailboxID, lockID, cvID);
		SEND_AND_RECEIVE();
		return 1;
#else
		LockTableLock->Acquire();
		CVTableLock->Acquire();
		CVTable[cvID].waitCount++;
		LockTable[lockID].lock->Acquire();
		
		LockTableLock->Release();
		CVTableLock->Release();
		//wait here
		//printf("Waiting on CV[%d].\n", cvID);
		CVTable[cvID].cv->Wait(LockTable[lockID].lock);
		//woke up here reacquire the CV table lock
		CVTableLock->Acquire();
		CVTable[cvID].waitCount--;
		
		// Check if condition needs to be deleted
		if ( CVTable[cvID].cv->isEmpty() && CVTable[cvID].isToBeDeleted && CVTable[cvID].waitCount == 0)
		{
			DEBUG('a', "Deleting cv [%d].\n", cvID);
			CVTable[cvID].addrSpace = 0;
			CVTable[cvID].isDeleted = true;
			CVTable[cvID].isToBeDeleted = false;
			delete CVTable[cvID].cv;
			CVTable[cvID].cv = NULL;
		}
		CVTableLock->Release();
		return 1; 
#endif
	}
	else
	{
		return 0;
	}
}

/*
 * This method is called to signal the cv with the given cvID and lockID:
 * 1. First acquire the cv table lock and the lock table lock. 
 * 2. First check if this is a valid cv / lock, if so, signal the CV. 
 * 3. Check if the CV has been marked for deletion if so, delete the cv
 * 4. Else if the cv / lock is not valid, return 0, failed operation.  
*/
int SignalCV_Syscall(int cvID, int lockID)
{
	if (LockValidation(lockID) && CVValidation(cvID))
	{
#ifdef NETWORK
		char outgoingMsg[MaxMailSize];
		sprintf(outgoingMsg,"%d^%d^%d^%d", SC_SignalCondition, currentThread->mailboxID, lockID, cvID);
		SEND_AND_RECEIVE();
		return 1;
#else		
		LockTableLock->Acquire();
		CVTableLock->Acquire();
		//printf("Signaling CV[%d].\n", cvID);
		CVTable[cvID].cv->Signal(LockTable[lockID].lock);
		
		// Check if condition needs to be deleted
		if ( CVTable[cvID].cv->isEmpty() && CVTable[cvID].isToBeDeleted && CVTable[cvID].waitCount == 0)
		{
			DEBUG('a', "Deleting cv [%d].\n", cvID);
			CVTable[cvID].addrSpace = 0;
			CVTable[cvID].isDeleted = true;
			CVTable[cvID].isToBeDeleted = false;
			delete CVTable[cvID].cv;
			CVTable[cvID].cv = NULL;
		}
		LockTableLock->Release();
		CVTableLock->Release();
		return 1; 
#endif //END NETWORK
	}
	else
	{
		return 0;
	}
}

/*
 * This method is called to broadcast on the cv with the given cvID and lockID:
 * 1. First acquire the cv table lock and the lock table lock. 
 * 2. First check if this is a valid cv / lock, if so, broadcast on the CV. 
 * 3. Check if the CV has been marked for deletion if so, delete the cv
 * 4. Else if the cv / lock is not valid, return 0, failed operation.  
*/
int BroadcastCV_Syscall(int cvID, int lockID)
{
	if (LockValidation(lockID) && CVValidation(cvID))
	{
#ifdef NETWORK
		char outgoingMsg[MaxMailSize];
		sprintf(outgoingMsg,"%d^%d^%d^%d", SC_BroadcastCondition, currentThread->mailboxID, lockID, cvID);
		SEND_AND_RECEIVE();
		return 1;
#else		
		LockTableLock->Acquire();
		CVTableLock->Acquire();
		//printf("Broadcast CV[%d].\n", cvID);
		CVTable[cvID].cv->Broadcast(LockTable[lockID].lock);
		
		// Check if condition needs to be deleted
		if ( CVTable[cvID].cv->isEmpty() && CVTable[cvID].isToBeDeleted && CVTable[cvID].waitCount == 0)
		{
			DEBUG('a', "Deleting cv [%d].\n", cvID);
			CVTable[cvID].addrSpace = 0;
			CVTable[cvID].isDeleted = true;
			CVTable[cvID].isToBeDeleted = false;
			delete CVTable[cvID].cv;
			CVTable[cvID].cv = NULL;
		}
		LockTableLock->Release();
		CVTableLock->Release();
		return 1; 
#endif
	}
	else
	{
		return 0;
	}
}

/*
 * This method is called to print to the screen. This method is the syscall for printf
 * 1. First acquire the print lock. 
 * 2. First check if the length of the string passed in is more than zero. If yes, allocate buffer.  
 * 3. User div and mod to extract the 2 arguments into 4
 * 4. Call printf with the proper arguments. 
 * 5. Else, length is zero or less, failed print.  
*/
int Print_Syscall(unsigned int str, int len, int arg0, int arg1)
{
	printLock->Acquire();
	if (len > 0)
	{
		int args[4] = {0, 0, 0, 0};
		
		char *buf = new char[len + 1];
		if( copyin(str, len, buf) == -1 )
		{
			DEBUG('a', "Cannot copy characters into the buffer\n");
			delete[] buf;
			return -1;
		}
		buf[len]='\0';
		
		args[0] = arg0 / 10000;
		args[1] = arg0 % 10000;
		args[2] = arg1 / 10000;
		args[3] = arg1 % 10000;
		
		printf(buf, args[0], args[1], args[2], args[3]);
		printLock->Release();	
		delete[] buf;
		return 1;
	}
	else
	{
		DEBUG('a', "String length is less than or equal to 0.\n");
		printLock->Release();
		return 0;
	}
}

/*
 * This method is used to print a string, without numbers.
 * string only!!!
*/
int PrintString_Syscall(unsigned int str, int len)
{
	printLock->Acquire();
	if (len <= 0)
	{
		DEBUG('a', "String length is less than or equal to zero.\n");
		printLock->Release();
		return 0;
	}
	char *buf = new char[len + 1];
	if( copyin(str, len, buf) == -1 )
	{
		DEBUG('a', "Cannot copy characters into the buffer\n");
		delete[] buf;
		return -1;
	}
	buf[len]='\0';
	printf(buf);
	printLock->Release();
	delete[] buf;
	return 1;
}

int Sprintf_Syscall(int target, int str, int len, int val)
{
	if (len <= 0)
	{
		DEBUG('a', "Length in Scan syscall is less than or equal to zero.\n");
		return -1;
	}
	char *buf = new char[len + 1];
	char *buf2 = new char[256];
	copyin(str, len + 1, buf);

	int arg1 = val / 10000;
	int arg2 = val % 10000;
	
	sprintf(buf2, buf, arg1, arg2);
	copyout(target, 256, buf2);
	return 1;
}

int Scan_Syscall(int vaddr, int len)
{
	if (len <= 0)
	{
		DEBUG('a', "Length in Scan syscall is less than or equal to zero.\n");
		return -1;
	}
	char *buf = new char[len];
	scanf("%s", buf);
	copyout(vaddr, len, buf);
	return 1;
}

int Atoi_Syscall(unsigned int str, int len)
{
	if (len <= 0)
	{
		DEBUG('a', "Length in Scan syscall is less than or equal to zero.\n");
		return -1;
	}
	
	char *buf = new char[len + 1];
	copyin(str, len + 1, buf);
	return atoi(buf);
}

void SRand_Syscall()
{
	srand(1);
}

int Rand_Syscall()
{	
	return rand();
}
//=================================================//
//---------------(END) CV SYSCALLS-----------------//
//=================================================//

//=================================================//
//----------------MONITOR SYSCALLS-----------------//
//=================================================//
bool MonitorValidation(int monitorID)
{
	if(monitorID < 0 || monitorID >= 1000)
	{
		DEBUG('a', "Invalid monitor");
		return false;
	}
	return true;
}

int CreateMonitor_Syscall(unsigned int name, int len, int arraySize)
{
#ifdef NETWORK
	if (len <= 0 || arraySize < 0 || arraySize >100)
	{
		DEBUG('a', "Cannot allocate Monitor with bad len or arraySize");
		return -1;
	}
	char *monitorName = new char[len + 1];
	if (copyin(name, len + 1, monitorName) == -1)
	{
		DEBUG('a', "copyin failed in CreateMonitor syscall\n");
		delete[] monitorName;
		return -1;
	}
	monitorName[len] = '\0';	
	char outgoingMsg[MaxMailSize];
	sprintf(outgoingMsg, "%d^%d^%s^%d", SC_CreateMonitor, currentThread->mailboxID, monitorName, arraySize);
	SEND_AND_RECEIVE();
	return atoi(incomingMsg);
#endif
	return -1;
}

int DeleteMonitor_Syscall (int monitorID)
{
#ifdef NETWORK
	if (!MonitorValidation(monitorID))
	{
		return -1;
	}
	
	char outgoingMsg[MaxMailSize];
	sprintf(outgoingMsg, "%d^%d^%d", SC_DeleteMonitor, currentThread->mailboxID, monitorID);
	SEND_AND_RECEIVE();
	return 1;
#endif
	return -1;
}


int GetMonitor_Syscall (int monitorID, int index)
{
#ifdef NETWORK
	if (!MonitorValidation(monitorID))
	{
		return -1;
	}
	char outgoingMsg[MaxMailSize];
	sprintf(outgoingMsg, "%d^%d^%d^%d", SC_GetMonitor, currentThread->mailboxID, monitorID, index);
	SEND_AND_RECEIVE();
	return atoi(incomingMsg);
#endif
	return -1;
}

void SetMonitor_Syscall(int monitorID, int index, int val)
{
#ifdef NETWORK
	if (!MonitorValidation(monitorID))
	{
		return;
	}
	printf("Monitor ID [%d], index [%d], val [%d].\n", monitorID, index, val);
	char outgoingMsg[MaxMailSize];
	sprintf(outgoingMsg, "%d^%d^%d^%d^%d", SC_SetMonitor, currentThread->mailboxID, monitorID, index, val);
	SEND_AND_RECEIVE();
#endif
	return;
}

void StartClient_Syscall()
{
#ifdef NETWORK
	char outgoingMsg[MaxMailSize];
	sprintf(outgoingMsg, "%d^%d", SC_StartClient, currentThread->mailboxID);
	SEND_AND_RECEIVE();	
	return;
#endif
}
//=================================================//
//-------------(END) MONITOR SYSCALLS--------------//
//=================================================//

void ExceptionHandler(ExceptionType which) {
    int type = machine->ReadRegister(2); // Which syscall?
    int rv=0; 	// the return value from a syscall

    if ( which == SyscallException ) {
	switch (type) {
	    default:
		DEBUG('a', "Unknown syscall - shutting down.\n");
	    case SC_Halt:
		DEBUG('a', "Shutdown, initiated by user program.\n");
		interrupt->Halt();
		break;
	    case SC_Create:
		DEBUG('a', "Create syscall.\n");
		Create_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
	    case SC_Open:
		DEBUG('a', "Open syscall.\n");
		rv = Open_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
	    case SC_Write:
		DEBUG('a', "Write syscall.\n");
		Write_Syscall(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      machine->ReadRegister(6));
		break;
	    case SC_Read:
		DEBUG('a', "Read syscall.\n");
		rv = Read_Syscall(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      machine->ReadRegister(6));
		break;
	    case SC_Close:
		DEBUG('a', "Close syscall.\n");
		Close_Syscall(machine->ReadRegister(4));
		break;
		
		case SC_Fork:
		DEBUG('c', "Fork syscall.\n");
		rv = Fork_Syscall(machine->ReadRegister(4),
							machine->ReadRegister(5),
							machine->ReadRegister(6));
		break;
		case SC_Yield:
		DEBUG('a', "Yield syscall.\n");
		Yield_Syscall();
		break;
		case SC_CreateLock:
		DEBUG('a', "CreateLock syscall.\n");
		rv = CreateLock_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
		case SC_DeleteLock:
		DEBUG('a', "DeleteLock syscall.\n");
		rv = DeleteLock_Syscall(machine->ReadRegister(4));
		break;
		case SC_AcquireLock:
		DEBUG('a', "AcquireLock syscall.\n");
		rv = AcquireLock_Syscall(machine->ReadRegister(4));
		break;
		case SC_ReleaseLock:
		DEBUG('a', "AcquireLock syscall.\n");
		rv = ReleaseLock_Syscall(machine->ReadRegister(4));
		break;
		case SC_CreateCondition:
		DEBUG('a', "CreateCV syscall.\n");
		rv = CreateCV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
		case SC_DeleteCondition:
		DEBUG('a', "DeleteCV syscall.\n");
		rv = DeleteCV_Syscall(machine->ReadRegister(4));
		break;
		case SC_WaitCondition:
		DEBUG('a', "WaitCV syscall.\n");
		rv = WaitCV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
		case SC_SignalCondition:
		DEBUG('a', "SignalCV syscall.\n");
		rv = SignalCV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
		case SC_BroadcastCondition:
		DEBUG('a', "BroadcastCV syscall.\n");
		rv = BroadcastCV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
		case SC_Print:
		DEBUG('c', "Print syscall.\n");
		rv = Print_Syscall(machine->ReadRegister(4),
						   machine->ReadRegister(5),
					       machine->ReadRegister(6),
						   machine->ReadRegister(7));
		break;
		case SC_PrintString:
		DEBUG('c', "PrintString syscall.\n");
		rv = PrintString_Syscall(machine->ReadRegister(4),
					   		machine->ReadRegister(5));
		break;
		case SC_Exec:
		DEBUG('c', "Exec syscall.\n");
		rv = Exec_Syscall(machine->ReadRegister(4),
					   		machine->ReadRegister(5));
		break;
		case SC_Exit:
		DEBUG('a', "Exit syscall.\n");
		Exit_Syscall(machine->ReadRegister(4));
		break;
		case SC_Sprintf:
		DEBUG('a', "Sprintf syscall.\n");
		rv = Sprintf_Syscall(machine->ReadRegister(4),
						   machine->ReadRegister(5),
					       machine->ReadRegister(6),
						   machine->ReadRegister(7));
		break;
		case SC_Scan:
		DEBUG('c', "Scan syscall.\n");
		rv = Scan_Syscall(machine->ReadRegister(4),
					   		machine->ReadRegister(5));
		break;
		case SC_Atoi:
		DEBUG('c', "Scan syscall.\n");
		rv = Atoi_Syscall(machine->ReadRegister(4),
					   		machine->ReadRegister(5));
		break;
		case SC_SRand:
		DEBUG('a', "SRand syscall.\n");
		SRand_Syscall();
		break;
		case SC_Rand:
		DEBUG('a', "Rand syscall.\n");
		rv = Rand_Syscall();
		break;
		case SC_CreateMonitor:
		DEBUG('a', "CreateMonitor syscall.\n");
		rv = CreateMonitor_Syscall(machine->ReadRegister(4),
										machine->ReadRegister(5),
										machine->ReadRegister(6));
		break;
		case SC_DeleteMonitor:
		DEBUG('a', "DeleteMonitor syscall.\n");
		rv = DeleteMonitor_Syscall(machine->ReadRegister(4));
		break;
		case SC_GetMonitor:
		DEBUG('c', "GetMonitor syscall.\n");
		rv = GetMonitor_Syscall(machine->ReadRegister(4),
					   		machine->ReadRegister(5));
		break;
		case SC_SetMonitor:
		DEBUG('a', "SetMonitor syscall.\n");
		SetMonitor_Syscall(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      machine->ReadRegister(6));
		break;
		case SC_StartClient:
		DEBUG('a', "StartClient syscall.\n");
		StartClient_Syscall();
		break;
	}

	// Put in the return value and increment the PC
	machine->WriteRegister(2,rv);
	machine->WriteRegister(PrevPCReg,machine->ReadRegister(PCReg));
	machine->WriteRegister(PCReg,machine->ReadRegister(NextPCReg));
	machine->WriteRegister(NextPCReg,machine->ReadRegister(PCReg)+4);
	return;
    } else {
      cout<<"Unexpected user mode exception - which:"<<which<<"  type:"<< type<<endl;
      interrupt->Halt();
    }
}
